/*
  Copyright (c) 2008 Romain Gaucher <r@rgaucher.info>

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

        http://www.apache.org/licenses/LICENSE-2.0
                
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
*/

#ifndef INTNUMBER_H
#define INTNUMBER_H

#include <gmp.h>
#include <gmpxx.h>
#include <stdexcept>
#ifndef require
#define require(test,ept) do {if (!(test)) throw std::runtime_error(ept);} while(0);
#endif

/**
	Wrapper for the mpz_class object fixing the operator problems
*/
class IntNumber
{
  protected:
	mpz_class number;
	IntNumber* ptr() {
		return this;
	}

  public:
	IntNumber(int  i = 0)   : number(i) {}
	IntNumber(unsigned int  i)   : number(i) {}
	IntNumber(const mpz_class& n) : number(n) {}
	IntNumber(const IntNumber& n) : number(n.number) {}
	~IntNumber()                              {}

	IntNumber& operator=(const IntNumber& n) {
		number = n.number;
		return *this;
	}

	// Have to rebind the gmp stuff, not working properly in the gmp c++ binding!
	inline IntNumber& operator<< (const IntNumber& i) { 
		mpz_class new_number;
		mpz_mul_2exp(new_number.get_mpz_t(),number.get_mpz_t(),i.number.get_ui());
		number = new_number;
		return *this; 
	}
	inline IntNumber& operator>> (const IntNumber& i) { 
		mpz_class new_number;
		mpz_tdiv_q_2exp(new_number.get_mpz_t(),number.get_mpz_t(),i.number.get_ui());
		number = new_number;		
		return *this; 
	}

	// unary
	friend const IntNumber& operator+(const IntNumber& a);
	friend const IntNumber  operator-(const IntNumber& a);
	friend const IntNumber  operator~(const IntNumber& a);
	friend       IntNumber* operator&(IntNumber& a);
	friend       int        operator!(const IntNumber& a);

	friend const IntNumber& operator++(IntNumber& a);
	friend const IntNumber  operator++(IntNumber& a, int);
	friend const IntNumber& operator--(IntNumber& a);
	friend const IntNumber  operator--(IntNumber& a, int);

	// binary
	friend const IntNumber operator+ (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator- (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator* (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator/ (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator^ (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator& (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator| (const IntNumber& left, const IntNumber& right);
	friend const IntNumber operator% (const IntNumber& left, const IntNumber& right);

	friend IntNumber& operator+= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator-= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator*= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator/= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator^= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator&= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator|= (IntNumber& left, const IntNumber& right);
	friend IntNumber& operator<<=(IntNumber& left, const IntNumber& right);
	friend IntNumber& operator>>=(IntNumber& left, const IntNumber& right);
	friend IntNumber& operator%= (IntNumber& left, const IntNumber& right);


	friend const bool operator==(const IntNumber& left, const IntNumber& right);
	friend const bool operator!=(const IntNumber& left, const IntNumber& right);
	friend const bool operator>=(const IntNumber& left, const IntNumber& right);
	friend const bool operator<=(const IntNumber& left, const IntNumber& right);
	friend const bool operator> (const IntNumber& left, const IntNumber& right);
	friend const bool operator< (const IntNumber& left, const IntNumber& right);

	// ostream
	friend std::ostream& operator<<(std::ostream&, const IntNumber& );
};

// unary
const IntNumber& operator+(const IntNumber& a) {
	return a;
}
const IntNumber  operator-(const IntNumber& a) {
	return IntNumber(0 - (a.number));
}
const IntNumber  operator~(const IntNumber& a) {
	return IntNumber(~(a.number));
}
IntNumber* operator&(IntNumber& a) {
	return a.ptr();
}
int operator!(const IntNumber& a) {
	return !a.number;
}
const IntNumber& operator++(IntNumber& a) {
	a.number++; return a;
}
const IntNumber  operator++(IntNumber& a, int) { // postfix
	IntNumber b(a);
	a.number++;
	return b;
}
const IntNumber& operator--(IntNumber& a) {
	a.number--; return a;
}
const IntNumber  operator--(IntNumber& a, int) {
	IntNumber b(a);
	a.number--;
	return b;
}

// binary
const IntNumber operator+ (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number + right.number);
}
const IntNumber operator- (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number - right.number);
}
const IntNumber operator* (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number * right.number);
}
const IntNumber operator/ (const IntNumber& left, const IntNumber& right) {
	require(right.number != 0, "divide by zero");
	return IntNumber(left.number / right.number);
}
const IntNumber operator^ (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number ^ right.number);
}
const IntNumber operator& (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number & right.number);
}
const IntNumber operator| (const IntNumber& left, const IntNumber& right) {
	return IntNumber(left.number | right.number);
}
const IntNumber operator% (const IntNumber& left, const IntNumber& right) {
	require(right.number != 0, "modulo zero");
	return IntNumber(left.number % right.number);
}

//
IntNumber& operator+= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number += right.number;
	return left;
}
IntNumber& operator-= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number -= right.number;
	return left;
}
IntNumber& operator*= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number *= right.number;
	return left;
}
IntNumber& operator/= (IntNumber& left, const IntNumber& right) {
	require(right.number != 0, "divide by zero");
	if(&left == &right) {/* self-assignment */}
	left.number /= right.number;
	return left;
}
IntNumber& operator^= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number ^= right.number;
	return left;
}
IntNumber& operator&= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number &= right.number;
	return left;
}
IntNumber& operator|= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number |= right.number;
	return left;
}
IntNumber& operator<<=(IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left  = (left << right);
	return left;
}
IntNumber& operator>>=(IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left = (left << right);
	return left;
}
IntNumber& operator%= (IntNumber& left, const IntNumber& right) {
	if(&left == &right) {/* self-assignment */}
	left.number %= right.number;
	return left;
}

// comparison
const bool operator==(const IntNumber& left, const IntNumber& right) {
	return left.number == right.number;
}
const bool operator!=(const IntNumber& left, const IntNumber& right) {
	return left.number != right.number;
}
const bool operator>=(const IntNumber& left, const IntNumber& right) {
	return left.number >= right.number;
}
const bool operator<=(const IntNumber& left, const IntNumber& right) {
	return left.number <= right.number;
}
const bool operator> (const IntNumber& left, const IntNumber& right) {
	return left.number > right.number;
}
const bool operator< (const IntNumber& left, const IntNumber& right) {
	return left.number < right.number;
}

// stream
std::ostream& operator<<(std::ostream& ost, const IntNumber& n) {
	ost << n.number;
	return ost;
}


#endif
